package com.team.recovery.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.team.recovery.domain.dto.RescueRequestDTO;
import com.team.recovery.domain.vo.RescueResponseVO;
import com.team.recovery.service.RescueService;
import org.springframework.data.geo.Circle;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Metrics;
import org.springframework.data.geo.Point;
import org.springframework.stereotype.Service;
import com.team.recovery.domain.BizRescueTask;
import com.team.recovery.domain.BizMerchantInfo;
import com.team.recovery.domain.vo.WebSocketMessageVO;
import com.team.recovery.mapper.BizRescueTaskMapper;
import com.team.recovery.mapper.BizMerchantInfoMapper;
import org.redisson.api.RedissonClient;
import org.redisson.api.RLock;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.GeoOperations;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 救援服务实现
 */
@Service
public class RescueServiceImpl implements RescueService {
    @Autowired
    private BizRescueTaskMapper rescueTaskMapper;
    @Autowired
    private BizMerchantInfoMapper merchantInfoMapper;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private SimpMessagingTemplate simpMessagingTemplate; // WebSocket推送

    private static final String GEO_MERCHANT_KEY = "geo:merchant";
    private static final String RESCUE_DELAY_QUEUE = "rescue.delay.queue";
    private static final String RESCUE_LOCK_PREFIX = "rescue:lock:";

    /**
     * 用户发起救援请求
     */
    @Override
    @Transactional
    public RescueResponseVO handleRescueRequest(RescueRequestDTO requestDTO) {
        // 1. 保存救援任务
        BizRescueTask task = BeanUtil.toBean(requestDTO, BizRescueTask.class);
        task.setStatus("0"); // 0待抢单
        task.setCreateTime(new Date());
        rescueTaskMapper.insert(task);

        // 2. 查询5公里内商家（Redis GEO）
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
        // 查询5公里内所有商家ID
        List<Long> merchantIds = new ArrayList<>();
        RedisGeoCommands.GeoRadiusCommandArgs args = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs();
        // 兼容Spring Data Redis 2.x/3.x写法
        List<RedisGeoCommands.GeoLocation<String>> merchants = new ArrayList<>();
        geoOps.radius(GEO_MERCHANT_KEY,
                new Circle(
                        new Point(requestDTO.getLongitude().doubleValue(), requestDTO.getLatitude().doubleValue()),
                        new Distance(5, Metrics.KILOMETERS)))
                .getContent().forEach(r -> merchantIds.add(Long.valueOf(r.getContent().getName())));

        // 3. 推送WebSocket消息给商家
        WebSocketMessageVO wsMsg = new WebSocketMessageVO();
        wsMsg.setType("rescue_push");
        Map<String, Object> data = new HashMap<>();
        data.put("taskId", task.getId());
        data.put("location", task.getLocation());
        data.put("type", task.getType());
        wsMsg.setData(data);
        for (Long merchantId : merchantIds) {
            simpMessagingTemplate.convertAndSend("/topic/merchant/" + merchantId, wsMsg);
        }

        // 4. 发送RabbitMQ延迟消息（5分钟后扩大范围）
        Map<String, Object> delayMsg = new HashMap<>();
        delayMsg.put("taskId", task.getId());
        delayMsg.put("radius", 10); // 扩大到10公里
        rabbitTemplate.convertAndSend(RESCUE_DELAY_QUEUE, delayMsg, message -> {
            message.getMessageProperties().setDelay(5 * 60 * 1000); // 5分钟延迟
            return message;
        });

        // 5. 返回响应
        RescueResponseVO resp = new RescueResponseVO();
        resp.setTaskId(task.getId());
        resp.setStatus(task.getStatus());
        resp.setMerchantIds(merchantIds);
        return resp;
    }

    /**
     * 商家接单（正式业务实现，可被REST接口或WebSocket调用）
     * @param taskId 救援任务ID
     * @param merchantId 商家ID
     * @return 是否接单成功
     */
    @Override
    public boolean acceptRescue(Long taskId, Long merchantId) {
        String lockKey = RESCUE_LOCK_PREFIX + taskId;
        RLock lock = redissonClient.getLock(lockKey);
        try {
            if (lock.tryLock(5, 10, TimeUnit.SECONDS)) {
                BizRescueTask task = rescueTaskMapper.selectByPrimaryKey(taskId);
                if (task == null || !"0".equals(task.getStatus())) {
                    return false; // 已被接单或不存在
                }
                task.setShopId(merchantId);
                task.setStatus("1"); // 已接单
                task.setUpdateTime(new Date());
                rescueTaskMapper.updateByPrimaryKey(task);
                // 推送WebSocket消息给用户
                WebSocketMessageVO wsMsg = new WebSocketMessageVO();
                wsMsg.setType("rescue_accept");
                Map<String, Object> data = new HashMap<>();
                data.put("taskId", taskId);
                data.put("merchantId", merchantId);
                wsMsg.setData(data);
                simpMessagingTemplate.convertAndSend("/topic/user/" + task.getUserId(), wsMsg);
                return true;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
        return false;
    }

    /**
     * 批量同步所有商家地理位置到Redis GEO
     */
    @Override
    public void syncAllMerchantGeoToRedis() {
        List<BizMerchantInfo> merchantList = merchantInfoMapper.selectAll();
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
        for (BizMerchantInfo merchant : merchantList) {
            if (merchant.getLongitude() != null && merchant.getLatitude() != null) {
                geoOps.add(GEO_MERCHANT_KEY,
                        new Point(merchant.getLongitude().doubleValue(), merchant.getLatitude().doubleValue()),
                        merchant.getId().toString());
            }
        }
    }

    /**
     * 新增或更新单个商家地理位置到Redis GEO
     */
    @Override
    public void addOrUpdateMerchantGeo(Long merchantId, double longitude, double latitude) {
        GeoOperations<String, String> geoOps = redisTemplate.opsForGeo();
        geoOps.add(GEO_MERCHANT_KEY, new Point(longitude, latitude), merchantId.toString());
    }
} 